package indi.cyh.fastservice.tool;

import com.alibaba.fastjson.JSONObject;

import indi.cyh.fastservice.base.entity.http.ParamMode;
import indi.cyh.fastservice.base.entity.http.RestParam;
import indi.cyh.fastservice.base.entity.http.RestService;
import indi.cyh.fastservice.config.RestServiceConfig;
import indi.cyh.jdbctool.tool.StringTool;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * rest请求工具类
 *
 * @ClassName RestRequestTool
 * @Description TODO
 * @Author cyh
 * @Date 2021/2/26 0026 11:57
 */
@Component
@Slf4j
public class RequstConfigServiceTool {
    @Autowired
    RequestTool requestTool;

    @Autowired
    RestServiceConfig serviceConfig;


    public RestService getRestServiceByName(String name) {
        List<RestService> restServiceList = serviceConfig.getRestServiceList();
        RestService service = restServiceList.stream().filter(restService -> name.equals(restService.getName())).findFirst().orElse(null);
        if (service == null) {
            throw new RuntimeException("获取指定rest服务配置失败!");
        }
        return service;
    }

    public Object request(String requestName, Object responseParam, Map<String, String> urlParam) throws Exception {
        requestName = requestName.trim();
        //从配置文件中获取获取接口信息
        RestService service = getRestServiceByName(requestName);
        //反射获取请求类中的参数
        Map<String, Map<String, Object>> param = getResponeParam(responseParam, service.getRequestParam(), service.getHeaderParam());
        //参数检查
        Map<String, Object> bodyParamMap = param.get("bodyParamMap");
        Map<String, Object> headerParamMap = param.get("headerParamMap");
        if (bodyParamMap.keySet().size() != 0) {
            paramChecking(service.getRequestParam(), service.getName(), bodyParamMap);
        }
        if (headerParamMap.keySet().size() != 0) {
            paramChecking(service.getHeaderParam(), service.getName(), headerParamMap);
        }
        //替换url参数
        String requestUrl = replaceUrlParam(service.getRUrl(), urlParam);
        //开始请求
        log.info("开始调用接口: " + service.getName());
        log.info("调用地址: " + service.getRequestUrl());
        HttpUriRequest request;
        String mode = service.getRequestMode().trim().toUpperCase();
        switch (mode) {
            case "GET":
                log.info("调用方式: GET");
                request = requestTool.get(makeUrlByParam(requestUrl, bodyParamMap), headerParamMap);
                break;
            case "POST":
                log.info("调用方式: POST");
                request = requestTool.post(requestUrl, bodyParamMap, headerParamMap, ParamMode.getParamMode(service.getParamMode()));
                break;
            default:
                throw new Exception("未实现的请求方式" + mode);
        }
        return handleResponse(request, service, requestName);
    }

    private Object handleResponse(HttpUriRequest request, RestService service, String requestName) throws Exception {
        // 创建Httpclient对象
        CloseableHttpClient httpclient = HttpClients.createDefault();
        //response 对象
        CloseableHttpResponse response = null;
        try {
            // 执行http get请求
            response = httpclient.execute(request);
            // 判断返回状态是否为200
            int code = response.getStatusLine().getStatusCode();
            //返回结果
            String res = EntityUtils.toString(response.getEntity(), "UTF-8");
            if (code == 200) {
                log.info("接口返回: {}", res);
                if (!StringTool.isEmpty(res)) {
                    String resType = service.getResponseDataType().trim();
                    if (StringTool.isEmpty(service.getResponseDataType())) {
                        return JSONObject.parseObject(res);
                    } else {
                        return JSONObject.parseObject(res, Thread.currentThread().getContextClassLoader().loadClass(resType));
                    }
                }
                return new JSONObject();
            }
            throw new Exception(requestName + "服务请求异常(" + code + "):" + res);
        } finally {
            if (response != null) {
                response.close();
            }
            httpclient.close();
        }
    }

    private String replaceUrlParam(String url, Map<String, String> urlParam) {
        if (urlParam == null) {
            return url;
        }
        for (String key : urlParam.keySet()) {
            url = url.replace("{{" + key.toUpperCase() + "}}", urlParam.get(key));
        }
        return url;
    }

    private Map<String, Map<String, Object>> getResponeParam(Object response, List<RestParam> bodyParamList, List<RestParam> headerParamList) throws IllegalAccessException {
        Map<String, Object> bodyParamMap = new HashMap<>();
        Map<String, Object> headerParamMap = new HashMap<>();
        Field[] fields = response.getClass().getDeclaredFields();
        for (RestParam param : bodyParamList) {
            String filedName = param.getKey();
            for (Field field : fields) {
                if (field.getName().equals(filedName)) {
                    boolean accessFlag = field.isAccessible();
                    field.setAccessible(true);
                    Object value = field.get(response);
                    bodyParamMap.put(filedName, value);
                    field.setAccessible(accessFlag);
                    break;
                }
            }
        }
        //获取头部参数
        for (RestParam param : headerParamList) {
            String filedName = param.getKey();
            for (Field field : fields) {
                if (field.getName().equals(filedName)) {
                    boolean accessFlag = field.isAccessible();
                    field.setAccessible(true);
                    Object value = field.get(response);
                    headerParamMap.put(filedName, value);
                    field.setAccessible(accessFlag);
                    break;
                }
            }
        }
        Map<String, Map<String, Object>> paramMap = new HashMap<>();
        paramMap.put("bodyParamMap", bodyParamMap);
        paramMap.put("headerParamMap", headerParamMap);
        return paramMap;
    }

    /**
     * 制作 url参数
     *
     * @param url
     * @param param
     * @return java.lang.String
     * @author CYH
     * @date 2020/12/16 0016 17:43
     **/
    public String makeUrlByParam(String url, Map<String, Object> param) {
        boolean isFirst = true;
        StringBuilder urlBuilder = new StringBuilder(url);
        for (String key : param.keySet()) {
            String value = param.get(key).toString();
            String urlParam = key + "=" + value;
            if (isFirst) {
                isFirst = false;
                urlBuilder.append("?");
            } else {
                urlBuilder.append("&");
            }
            urlBuilder.append(urlParam);
        }
        String res = urlBuilder.toString();
        log.info("URL参数: {}", res);
        return res;
    }

    private void paramChecking(List<RestParam> targetParam, String serviceName, Map<String, Object> param) throws Exception {
        for (RestParam restParam : targetParam) {
            String key = restParam.getKey();
            Object defaultValue = restParam.getDefaultValue();
            if (restParam.isRequired()) {
                if (param.containsKey(key)) {
                    if (param.get(key) == null) {
                        setParamDefaultValue(param, key, defaultValue, serviceName);
                    }
                } else {
                    setParamDefaultValue(param, key, defaultValue, serviceName);
                }
            }
        }
    }

    private void setParamDefaultValue(Map<String, Object> param, String key, Object defaultValue, String name) throws Exception {
        if (param.get(key) == null) {
            if (defaultValue != null) {
                //必填、参数没键，有默认值
                param.put(key, defaultValue);
            } else {
                //必填、参数没键，没默认值
                throw new Exception("调用接口" + name + "时,缺少参数:" + key);
            }
        }
    }
}

